﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using System.ComponentModel;

using Microsoft.Practices.Unity;
using Microsoft.Win32;

using ObjectEditor;
using ObjectEditor.Display3D;
using ObjectEditor.Editor;

using EQEmu.Doors;
using EQEmu.Doors.Display3D;
using EQEmu.Database;

namespace EQEmu.DoorsPlugin
{
    internal class DoorsVisual3D : ModelVisual3D
    {
    }

    public class DoorsRibbonTabViewModel : ViewModel, IWorldModel3DProvider, IObjectEditor, IDisposable
    {
        public event EventHandler<ModelChangedEventArgs> ModelChanged;
        public event EventHandler<CameraChangeEventArgs> RequestCameraChange;
        public event EventHandler<ObjectSelectedEventArgs> ObjectSelected;

        private Transform3D _transform;

        private string _zoneShortName;
        private int _zoneVersion;
        private ZoneDoors _zoneDoors;
        private ZoneDoorsDisplay3D _display3d;
        private ModelVisual3D _modelVisual;
        private doors _selectedDoor;
        private double _zAdjustment;
        private bool _useDatabase;
        private IEnumerable<doors> _multipleSelections = null;
        private EQEmu.Files.WLD.WLD _wldFile = null;
        private EQEmu.Files.WLD.Fragments.Mesh _selectedMesh;

        private DelegateCommand _saveFileCommand;
        private DelegateCommand _openFileCommand;
        private DelegateCommand _searchZoneCommand;
        private DelegateCommand _removeDoorCommand;
        private DelegateCommand _addDoorCommand;
        private DelegateCommand _commitChangesCommand;
        private DelegateCommand _setMeshCommand;

        public DoorsRibbonTabViewModel()
        {
            ZoneVersion = 0;
            ZAdjustment = 0;
        }

        [Dependency]
        public EQEmuContextFactory EntitiesFactory
        {
            get;
            set;
        }

        private EQEmu.Files.WLD.WLD WldFile
        {
            get { return _wldFile; }
            set
            {
                _wldFile = value;
                if (value != null)
                {
                    _wldFile.ResolveMeshNames();
                }
                OnPropertyChanged("Meshes");
            }
        }

        private bool CheckDBConnection()
        {
            if (_useDatabase == false) return false;
            if (EntitiesFactory == null) return false;
            else
            {
                try
                {
                    var context = EntitiesFactory.Create();
                    var query = context.doors.Where(x => x.id == 0).ToList();
                    context.Dispose();
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        public static string ZoneDoorsFileFilter
        {
            get
            {
                return String.Format("ZoneDoors Files ({1}.{0})|*{1}.{0}|Object Files ({2}.{3})|*{2}.{3}|All Files (*.*)|*.*", "xml", "zonedoors","_obj","s3d");
            }
        }

        public IEnumerable<EQEmu.Files.WLD.Fragments.Mesh> Meshes
        {
            get
            {
                if (_wldFile != null)
                {
                    return _wldFile.ZoneMeshes;
                }
                else
                {
                    return null;
                }
            }
        }

        public EQEmu.Files.WLD.Fragments.Mesh SelectedMesh
        {
            get { return _selectedMesh; }
            set
            {
                _selectedMesh = value;
                OnPropertyChanged("SelectedMesh");
                SetMeshCommand.RaiseCanExecuteChanged();
            }
        }

        public DelegateCommand SetMeshCommand
        {
            get 
            {
                if (_setMeshCommand == null)
                {
                    _setMeshCommand = new DelegateCommand(
                        x =>
                        {
                            string rem = "_DMSPRITEDEF";
                            SelectedDoor.name = SelectedMesh.FragmentName.Replace(rem, "");
                        },
                        y =>
                        {
                            return SelectedDoor != null && SelectedMesh != null;
                        });
                }
                return _setMeshCommand;
            }
        }

        public DelegateCommand CommitChangesCommand
        {
            get
            {
                if (_commitChangesCommand == null)
                {
                    _commitChangesCommand = new DelegateCommand(
                        x =>
                        {
                            var db = _zoneDoors as ZoneDoorsDatabase;

                            var result = 
                                System.Windows.MessageBox.Show("Commit changes to database?", "Commit changes", System.Windows.MessageBoxButton.YesNo);
                            if (result == System.Windows.MessageBoxResult.Yes)
                            {
                                if (db != null)
                                {
                                    try
                                    {
                                        db.CommitChanges();
                                    }
                                    catch (Exception e)
                                    {
                                        System.Windows.MessageBox.Show("Failed committing changes:" + e.Message + "," + e.StackTrace);
                                    }
                                }
                            }
                        },
                        y =>
                        {
                            return _zoneDoors != null && (_zoneDoors as ZoneDoorsDatabase) != null;
                        });
                }
                return _commitChangesCommand;
            }
        }

        public DelegateCommand CreateDoorCommand
        {
            get
            {
                if (_addDoorCommand == null)
                {
                    _addDoorCommand = new DelegateCommand(
                        x =>
                        {
                            var door = _zoneDoors.CreateDoor();
                            _zoneDoors.AddDoor(door);
                            SelectedDoor = door;
                            _multipleSelections = null;
                        },
                        y =>
                        {
                            return _zoneDoors != null;
                        });
                }
                return _addDoorCommand;
            }
        }

        public DelegateCommand RemoveDoorCommand
        {
            get
            {
                if (_removeDoorCommand == null)
                {
                    _removeDoorCommand = new DelegateCommand(
                        x =>
                        {
                            if (_selectedDoor != null)
                            {
                                _zoneDoors.RemoveDoor(_selectedDoor);
                                if (_multipleSelections != null)
                                {
                                    foreach (var door in _multipleSelections.Where(y => y != _selectedDoor))
                                    {
                                        _zoneDoors.RemoveDoor(door);
                                    }
                                }
                            }
                            SelectedDoor = null;
                            _multipleSelections = null;
                        },
                        y =>
                        {
                            return _zoneDoors != null && (_selectedDoor != null || _multipleSelections != null);
                        });
                }
                return _removeDoorCommand;
            }
        }

        public DelegateCommand SaveFileCommand
        {
            get
            {
                if (_saveFileCommand == null)
                {
                    _saveFileCommand = new DelegateCommand(
                        x =>
                        {
                            var fd = new SaveFileDialog();
                            fd.FileName = ZoneShortName + "." + ZoneVersion + ".zonedoors.xml";
                            fd.Filter = ZoneDoorsFileFilter;
                            if ((bool)fd.ShowDialog())
                            {
                                //_.SaveToFile(fd.FileName);
                            }
                        },
                        y =>
                        {
                            return _zoneDoors != null;
                        });
                }
                return _saveFileCommand;
            }
        }

        public DelegateCommand OpenFileCommand
        {
            get
            {
                if (_openFileCommand == null)
                {
                    _openFileCommand = new DelegateCommand(
                        x =>
                        {
                            var od = new OpenFileDialog();
                            od.Filter = ZoneDoorsFileFilter;
                            if ((bool)od.ShowDialog())
                            {
                                if (od.FileName.Contains(".s3d") || od.FileName.Contains(".wld"))
                                {
                                    try
                                    {                                        
                                        var s3d = EQEmu.Files.S3D.S3D.Load(od.FileName);
                                        var f = s3d.Files.FirstOrDefault(y => y.Name.Contains("_obj.wld"));
                                        using(var stream = new System.IO.MemoryStream(f.Bytes))
                                        {
                                            WldFile = EQEmu.Files.WLD.WLD.Load(stream, f.Name);
                                            if (WldFile != null)
                                            {
                                                WldFile.Files = s3d;
                                            }
                                        }

                                        if (_display3d != null)
                                        {
                                            _display3d.WldFile = WldFile;
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        System.Windows.MessageBox.Show("Could not load file:" + e.Message + Environment.NewLine + e.StackTrace);
                                    }
                                }
                                else
                                {
                                    //if (CheckDBConnection())
                                    //{
                                    //    try
                                    //    {
                                    //        //ZoneSpawns = EQEmu.Doors.ZoneSpawnsDatabase.LoadFromFile(od.FileName, EntitiesFactory);
                                    //    }
                                    //    catch (ArgumentException e)
                                    //    {
                                    //        System.Windows.MessageBox.Show("Could not load file:" + e.Message + Environment.NewLine + e.StackTrace);
                                    //    }
                                    //}
                                    //else
                                    //{
                                    //    //ZoneSpawns = EQEmu.Spawns.ZoneSpawns.LoadFromFile(od.FileName);
                                    //    //ZoneDoors = 
                                    //}
                                }
                            }
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _openFileCommand;
            }
        }

        public DelegateCommand SearchZoneCommand
        {
            get
            {
                if (_searchZoneCommand == null)
                {
                    _searchZoneCommand = new DelegateCommand(
                        x =>
                        {
                            LoadZoneData();
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _searchZoneCommand;
            }
        }

        private ZoneDoors ZoneDoors
        {
            get { return _zoneDoors; }
            set
            {
                if (value != null)
                {
                    if (_zoneDoors != null)
                    {
                        var disposer = _zoneDoors as IDisposable;
                        if (disposer != null) disposer.Dispose();
                    }

                    _zoneDoors = value;
                    SaveFileCommand.RaiseCanExecuteChanged();
                    if (_display3d != null)
                    {
                        _display3d.Dispose();
                    }
                    _display3d = new ZoneDoorsDisplay3D(_zoneDoors);
                    _display3d.WldFile = _wldFile;

                    Model3D = new DoorsVisual3D()
                    {
                        Content = _display3d.Model,
                        Transform = Transform3D
                    };

                    ZoneShortName = value.ZoneShortName;
                    ZoneVersion = value.ZoneVersion;
                }
                CreateDoorCommand.RaiseCanExecuteChanged();
                CommitChangesCommand.RaiseCanExecuteChanged();
            }
        }

        public bool UseDatabase
        {
            get { return _useDatabase; }
            set
            {
                _useDatabase = value;
                OnPropertyChanged("UseDatabase");
            }
        }

        public int ZoneVersion
        {
            get { return _zoneVersion; }
            set
            {
                _zoneVersion = value;
                OnPropertyChanged("ZoneVersion");
            }
        }

        private void LoadZoneData()
        {
            if (_zoneDoors != null && _zoneDoors.HasModifications)
            {
                var result =
                    System.Windows.MessageBox.Show("Discard all changes?", "Discard channges", System.Windows.MessageBoxButton.YesNo);
                if (result == System.Windows.MessageBoxResult.No) return;
            }

            if (UseDatabase)
            {
                try
                {
                    ZoneDoors = new ZoneDoorsDatabase(ZoneShortName, EntitiesFactory, ZoneVersion);
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("Could not created object:" + e.Message + Environment.NewLine + e.StackTrace);
                }
            }
            else
            {
                ZoneDoors = new ZoneDoors(ZoneShortName, ZoneVersion);
            }
        }

        private Point3D CenterPoint(IEnumerable<doors> doors)
        {
            var points = new List<Point3D>();
            foreach (var n in doors)
            {
                points.Add(new Point3D(n.pos_x, n.pos_y, n.pos_z));
            }

            return Common.Functions.Center(points);
        }

        public string ZoneShortName
        {
            get { return _zoneShortName; }
            set
            {
                _zoneShortName = value;
                OnPropertyChanged("ZoneShortName");
            }
        }

        public double ZAdjustment
        {
            get { return _zAdjustment; }
            set
            {
                _zAdjustment = value;
                OnPropertyChanged("ZAdjustment");
            }
        }

        public doors SelectedDoor
        {
            get { return _selectedDoor; }
            set
            {
                _selectedDoor = value;

                OnPropertyChanged("SelectedDoor");
                if (value != null)
                {
                    var point = _multipleSelections != null && _multipleSelections.Count() > 1 ?
                        this.CenterPoint(_multipleSelections) : new Point3D(value.pos_x, value.pos_y, value.pos_z);
                    var look = Transform3D.Transform(point);

                    OnObjectSelected(value, _multipleSelections);
                    OnCameraChange(new CameraChangeEventArgs()
                    {
                        LookAtPosition = look
                    });
                }
                else
                {
                    _multipleSelections = null;
                    OnObjectSelected(null, null);
                }
                RemoveDoorCommand.RaiseCanExecuteChanged();
                SetMeshCommand.RaiseCanExecuteChanged();
            }
        }

        [Dependency("EQEmu.Transform")]
        public Transform3D Transform3D
        {
            get { return _transform; }
            set
            {
                _transform = value;
            }
        }

        public ModelVisual3D Model3D
        {
            get { return _modelVisual; }
            private set
            {
                if (_modelVisual != null)
                {
                    _modelVisual.Children.Clear();
                }

                _modelVisual = value;
                OnPropertyChanged("Model3D");
                OnModelChanged();
            }        
        }

        protected void OnModelChanged()
        {
            var e = ModelChanged;
            if (e != null)
            {
                e(this, new ModelChangedEventArgs());
            }
        }

        protected void OnObjectSelected(INotifyPropertyChanged obj,IEnumerable<INotifyPropertyChanged> objs)
        {
            var e = ObjectSelected;
            if (e != null)
            {
                e(this, new ObjectSelectedEventArgs(obj,objs));
            }
        }

        protected void OnCameraChange(CameraChangeEventArgs change)
        {
            var e = RequestCameraChange;
            if (e != null)
            {
                e(this, change);
            }
        }        

        public void UserClicked(System.Windows.Media.Media3D.Point3D point, AppInformation info)
        {
            if (info.SelectedRibbonTab == null || (info.SelectedRibbonTab as DoorsRibbonTab) == null) return;
            if (_zoneDoors == null) return;

            var p = Transform3D.Transform(point);

            if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) && SelectedDoor != null)
            {
                var door = SelectedDoor;
                var center = new Point3D(SelectedDoor.pos_x, SelectedDoor.pos_y, SelectedDoor.pos_z);
                if (_multipleSelections != null && _multipleSelections.Count() > 1)
                {
                    center = this.CenterPoint(_multipleSelections);
                }

                double transX = p.X - center.X;
                double transY = p.Y - center.Y;
                double transZ = p.Z - center.Z + ZAdjustment;
                door.pos_x += (float)transX;
                door.pos_y += (float)transY;
                door.pos_z += (float)transZ;                

                if (_multipleSelections != null)
                {
                    foreach (var d in _multipleSelections.Where(x => x != door))
                    {
                        d.pos_x += (float)transX;
                        d.pos_y += (float)transY;
                        d.pos_z += (float)transZ;
                    }
                }

                SelectedDoor = door;

            }
            else if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && SelectedDoor != null)
            {
                //change the heading
                SelectedDoor.LookAt(p.X, p.Y, p.Z);
                if (_multipleSelections != null)
                {
                    foreach (var door in _multipleSelections.Where(x => x != SelectedDoor))
                    {
                        door.LookAt(p.X, p.Y, p.Z);
                    }
                }
            }
            else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            {
                var door = _zoneDoors.CreateDoor();
                door.pos_x = (float)p.X;
                door.pos_y = (float)p.Y;
                door.pos_z = (float)p.Z;
                _zoneDoors.AddDoor(door);
                SelectedDoor = door;
                _multipleSelections = null;
            }
            else
            {
                var door = _zoneDoors.GetClosestDoor(p,2);
                if (info.World3DSelectionBox != null)
                {
                    var lst = new List<doors>();
                    foreach (var d in _zoneDoors.Doors)
                    {
                        var tp = new Point3D(d.pos_x, d.pos_y, d.pos_z);
                        tp = Transform3D.Transform(tp);
                        if (info.World3DSelectionBox(tp, 5.0))
                        {
                            lst.Add(d);
                        }
                    }
                    _multipleSelections = lst;
                    if (door == null && _multipleSelections.Count() > 0)
                    {
                        door = _multipleSelections.ElementAt(0);
                    }
                }

                if (door != null)
                {
                    SelectedDoor = door;
                }
            }
        }

        public void ConstrainView(ViewConstraints constraints)
        {
            if (_display3d == null) return;

            _display3d.UpdateView(new EQEmu.Display3D.ViewConstraints()
            {
                MaxX = constraints.MaxX,
                MaxY = constraints.MaxY,
                MaxZ = constraints.MaxZ,
                MinX = constraints.MinX,
                MinY = constraints.MinY,
                MinZ = constraints.MinZ
            });
        }

        protected virtual void Dispose(bool disposeAll)
        {
            if (_display3d != null)
            {
                _display3d.Dispose();
            }

            if (_zoneDoors != null)
            {
                var disposer = _zoneDoors as IDisposable;
                if (disposer != null) disposer.Dispose();
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
    }
}
